use crate::core::config::Conf;
use std::io::{BufRead, BufReader, Write};
use std::net::TcpStream;
use std::str;
use std::collections::HashMap;
use chrono::{DateTime, Utc};
use crossbeam_channel::{unbounded,Sender};
//use std::sync::mpsc::{Sender, channel};
use std::thread;
use std::sync::{Arc, Mutex};
use lazy_static::lazy_static;
use std::time::{UNIX_EPOCH, Duration};
use std::fs::File;
use std::error::Error;
use log::{trace};

pub const KEY : &str = "40bbc689061fdce0";

lazy_static! {
    static ref STAT: Arc<Mutex<Status>> = Arc::new(Mutex::new(Status::new()));
}

pub struct Status {
    pub up_time : i64,  //启动日期，时间戳
    pub upto_now : i64, //启动天数
    pub topics : HashMap<String,(u64,u64)>, // topic,(today_num,upto_now_num)
}

impl std::fmt::Display for Status {
    fn fmt(&self,f : &mut std::fmt::Formatter ) -> std::fmt::Result {
        write!( f,
                "pkp status :  
                {{
                    up_time   : {:?},
                    upto_now  : {:?},
                    topics : {:?},
                }}",
                self.up_time,self.upto_now,self.topics
        )
    }
}

impl Status {
    
    pub fn new() -> Status {
        let stat = Status {
            up_time : Utc::now().timestamp(),
            upto_now : 0,
            topics : HashMap::new(),
        };
        stat
    }

    pub fn start_status_server() -> Sender<(String,u64)> {
        let (tx, rx) = unbounded::<(String,u64)>();
        let now: DateTime<Utc> = Utc::now();
        let mut last_mem_day = now.format("%Y-%m-%d").to_string();
        thread::spawn(move || {
            loop {
                match rx.recv() {
                    Ok(m) => {
                        let (topic,cnt) : (String,u64) = m;
                        let date = Utc::now().format("%Y_%m_%d").to_string();
                        let mut st = match STAT.lock() {
                            Ok(_st) => _st,
                                  _ => {
                                        trace!("satus STAT lock ~");
                                        continue;
                                    }
                        };
                        //换天处理
                        if date != last_mem_day {
                            last_mem_day = date;
                            for (_, val) in st.topics.iter_mut() {
                                val.0 = 0;
                            }
                        }
                        //天内处理
                        if let Some(x) = st.topics.get_mut(&topic) {
                            x.0 += cnt;
                            x.1 += cnt;
                        }else{
                            st.topics.insert(topic,(cnt,cnt));
                        }
                    },
                    _  => {
                        trace!("satus recv error ~");
                        continue;
                    }
                }
                
            }
        });

        tx
    }

    pub fn get_running_status(addr : String){
        let _addr =  match addr.is_empty() {
            true => {
                let conf = Conf::get_config(None);
                format!("{}:{}",conf.addr,conf.port)
            },
            false => {
                addr
            },
        };

        let mut stream = match TcpStream::connect(_addr) {
            Ok(stm) => stm,
            Err(_)  => {
                println!("Connection refused from proxy server ~");
                std::process::exit(0);
            }
        };
        let input = String::from(KEY);
        stream.write(input.as_bytes())
              .expect("Failed to connect the proxy server ~");

        let mut reader = BufReader::new(&stream);
        let mut buffer: Vec<u8> = Vec::new();
        reader.read_until(b'~', &mut buffer)
              .expect("Failed to read the status from proxy server ~");
        println!("{}",str::from_utf8(&buffer).expect("Failed to write buffer as string"));
    }

    pub fn show_status() -> Result<String,Box<dyn Error>> {
        let stat = STAT.lock()?;
        let now = Utc::now().timestamp();
        let pass = now - stat.up_time as i64;
        let upto_now = pass/86400;
        let d = UNIX_EPOCH + Duration::from_secs(stat.up_time as u64);
        let datetime = DateTime::<Utc>::from(d);
        let date = datetime.format("%Y-%m-%d %H:%M:%S").to_string();
        //mem
        let pid = std::process::id();
        let pflie = format!("/proc/{}/status",pid);
        let file = File::open(pflie)?;
        let file = BufReader::new(file);
        let mut mem : String = String::from("0");
        for line in file.lines() {
            let m = line?;
            match &m.find("VmRSS"){
                Some(_) => {
                    mem = m.to_owned();
                    break;
                },
                _ => continue,
            }
        }
        let mem : Vec<&str> = mem.split(' ').collect();
        let mut s_mem : usize = 0;
        for i in mem {
            match i.parse::<usize>() {
                Ok(_mem) => s_mem = _mem/1000,
                _ => continue,
            }
        }
        
        Ok(format!("pkp status on this server :
        {{
            启动时间   : {},
            运行天数   : {},
            占用内存   : {} MB,
            主题条目   :
            [topic: (当天生产,总生产数)] :  
            {:?},   
        }} ~",
        date,upto_now,s_mem,stat.topics))
    }

}